
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: XMLItem.java 377 2010-03-11 20:50:18Z archie.cobbs $
 */

package org.dellroad.sidekar.xml;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;

import org.dellroad.sidekar.Entity;
import org.dellroad.sidekar.Field;
import org.dellroad.sidekar.Item;
import org.dellroad.sidekar.util.SimpleIterator;

/**
 * Class associated with the &lt;item&gt; element in the JiBX binding context.
 */
class XMLItem {

    // Used during marshalling
    private String entityName;
    private long id;
    private Set<String> fieldNames;

    // Used during unmarshalling
    private Item<?> item;
    private XMLData xmlData;

    // Used during unmarshalling
    private XMLItem(XMLData xmlData) {
        if (xmlData == null)
            throw new IllegalArgumentException("null xmlData");
        this.xmlData = xmlData;
    }

    // Used during marshalling
    public XMLItem(Item<?> item) {
        this(item, null);
    }

    // Used during marshalling
    public XMLItem(Item<?> item, Set<String> fieldNames) {
        this.item = item;
        this.entityName = item.getSidekarEntity().getName();
        this.id = item.getSidekarId();
        this.fieldNames = fieldNames;
    }

    // Used during marshalling
    public Iterator<XMLField> iterator() {
        assert this.item != null;
        ArrayList<Field<?>> fields = new ArrayList<Field<?>>();
        for (Field<?> field : item.getSidekarEntity().getFields()) {
            if (this.fieldNames == null || this.fieldNames.contains(field.getName()))
                fields.add(field);
        }
        return new FieldIterator(this.item, fields);
    }

    // Used during unmarshalling
    public void add(XMLField xmlField) {

        // For simple fields (only), set the simple field's value in the item
        if (xmlField instanceof XMLSimpleField)
            xmlField.getField().visit(xmlField);
    }

    // Used during unmarshalling
    public void finish() {
        getItem();              // ensure item gets created
    }

    // Used during unmarshalling
    protected Item<?> getItem() {
        if (this.item == null) {
            Entity<?> entity = this.xmlData.getXMLDatabase().getDatabase().getEntity(this.entityName);
            this.item = (Item)entity.get(this.id);
            this.item.sidekarAttach();
        }
        return this.item;
    }

    public XMLData getXMLData() {
        return this.xmlData;
    }

    // Used during unmarshalling
    public static XMLItem newInstance(Object obj) {
        return new XMLItem((XMLData)obj);
    }

    // Iterates over the item's fields and outputs XMLField objects
    static class FieldIterator extends SimpleIterator<XMLField> {

        private final Iterator<Field<?>> i;
        private final XMLFieldSwitch fieldSwitch;

        private XMLField next;

        public FieldIterator(Item<?> item, Iterable<Field<?>> fields) {
            this.i = fields.iterator();
            this.fieldSwitch = new XMLFieldSwitch(item);
        }

        public boolean hasNext() {
            findNext();
            return this.next != null;
        }

        @Override
        protected XMLField getNext() {
            try {
                return this.next;
            } finally {
                this.next = null;
            }
        }

        private void findNext() {
            while (this.next == null && this.i.hasNext()) {
                Field<?> field = this.i.next();
                field.visit(this.fieldSwitch);
                this.next = this.fieldSwitch.getXMLField();
                if (this.next.hasDefaultValue())
                    this.next = null;
            }
        }
    }
}

